Say that unbalanced initialization errors come from initialization - #4897
Conversation
Structural analysis has no way of knowing that the system handed to it is
an initialization system, so an unbalanced or singular initialization
system was reported in terms that read as if the model itself were at
fault:
ERROR: ExtraVariablesSystemException: The system is unbalanced. There
are 2 highest order derivative variables and 1 equations.
More variables than equations, here are the potential extra variable(s):
inertia₊wˍt(t)
Nothing in that names initialization, says how many equations are missing,
or says how to supply them, and the `ˍt` names are derivatives the user
never wrote. The only hint is the stack trace.
Wrap the `mtkcompile` of the initialization system so these errors are
rethrown with that context: which system is unbalanced and in which
direction, how many equations short it is, and the ways of supplying them
(initial values in the problem constructor or the model,
`initialization_eqs`, or `fully_determined = false` to solve in a least
squares sense). The exception type is unchanged, so code catching these
still works.
The deficit is measured by recompiling without the balance check, which
happens only on the error path. Only the difference is reported: that
compile simplifies further, so its absolute counts do not line up with the
ones structural analysis reports.
`InvalidSystemException` also reports problems unrelated to the balance of
a system, so only its structural singularity message is rewritten.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XZQMmzJbrqB9aJomKAB9z
The remedies in an initialization error name ModelingToolkit functions and keyword arguments: pass this to `ODEProblem`, set `initialization_eqs`. A front end which presents a modelling language of its own has none of those, so the advice sends its users looking for something that does not exist in their language. Split each remedy into the part that is about the model and the part that is about the ModelingToolkit API, and add `show_api_guidance!(false)` to drop the latter. What went wrong is still described in full. A front end calls it once when it loads and adds guidance of its own. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XZQMmzJbrqB9aJomKAB9z
|
Added Each remedy is now split in two: what is true of the model, and what names ModelingToolkit functions and keyword arguments. Same error, guidance on (the default): and with Why a global toggle rather than SciMLLoggingSciMLLogging controls whether a message is emitted and at what level, keyed on a verbosity object threaded through the call. This text is not a message — it is part of an exception's message string, which is always emitted. Routing it through SciMLLogging would mean either logging the remedy separately from the error it explains (so it can be reordered or lost, and is worse for the common case), or using the verbosity object purely as a config carrier, which is not what it is for. It would also add a dependency to ModelingToolkitBase, which has neither SciMLLogging nor Preferences today. So this is a plain documented setter with process-wide effect, which is the shape that fits a host application setting it once at startup. If MTK does adopt a SciMLLogging verbosity spec later, this is one toggle to fold into it — the call sites read Alternatives, if either is preferred:
Documented under "Error message guidance" on the Debugging page. |
|
Make the global toggle be an experimental thing for now, the documented way will be via SciMLLogging but that hasn't landed yet so we'll want that to be the documented way to handle this, but this is good enough to get it shipped for now. |
Verbosity across the SciML ecosystem is moving to SciMLLogging.jl, and this setting belongs there once ModelingToolkit adopts it. Until then it ships as experimental and unsupported rather than as documented API, so replacing it is not a breaking change. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XZQMmzJbrqB9aJomKAB9z
|
Marked experimental in aade10f. Both docstrings now carry:
and the The call sites read |
Draft — please ignore until reviewed by @ChrisRackauckas.
The problem
Reported by Michael Tiller: a Dyad model built for the JuliaCon workshop failed with
The counting is correct and the fix is "supply one more equation", but nothing in the message says which system is unbalanced. The model's own equations are fine; it is the initialization system that is short an equation. The only indication of that is the stack trace. On top of that,
inertia₊wˍt(t)isD(inertia₊w), a variable the user never wrote, and the message gives no hint of how to supply the missing equation.Reproducer
Not Dyad-specific — plain MTK reproduces it, and shows the progression:
The change
InitializationProblemcompiles the initialization system at exactly one place. That call is now wrapped so structural errors are rethrown with the missing context. The exception type is unchanged, so existing code (and the existing@test_throwsin the suite) that catchesExtraVariablesSystemException/ExtraEquationsSystemException/InvalidSystemExceptionstill works — only the message changes.The same case now reports:
Three variants are handled, each with its own remedy paragraph:
ExtraVariablesSystemException→ underdeterminedExtraEquationsSystemException→ overdeterminedInvalidSystemException(singular message only) → structurally singular, i.e. balanced but with a redundant conditionNotes on two choices:
nunknowns - neqs. This costs a second compile, but only on the path that is about to throw. Only the difference is reported, not the absolute counts: that compile simplifies further than the one that failed, so its raw counts do not line up with the ones structural analysis prints, and showing both pairs would be worse than showing one.InvalidSystemExceptionis also thrown for things that have nothing to do with the balance of a system ("Illegal unknown: …", derivative on the RHS). Only messages that report structural singularity are rewritten; the rest pass through untouched.ExtraVariablesSystemExceptionand friends are defined both in ModelingToolkitBase and in StateSelection.jl, which ModelingToolkitBase does not depend on, so the two are matched by type name. The alternative is to thread a "this is an initialization system" flag frommtkcompiledown intoStateSelection.check_consistencyand build the message at the source, which would also let it printD(w)instead ofwˍtand drop the second compile. That is the better long-term shape but spans two repos; happy to do it that way instead if preferred.Tests
Added to
lib/ModelingToolkitBase/test/initializationsystem.jl, alongside the existing@test_throwscases that already cover both the under- and over-determined paths:Not addressed here
Michael also noted that the AI assistant diagnosed this as "just specify an initial condition for phi" and claimed to have verified it without running the analysis. That is an agent-side issue, not a ModelingToolkit one.
🤖 Generated with Claude Code
https://claude.ai/code/session_019XZQMmzJbrqB9aJomKAB9z